<template>
  <div class="i-editor-textarea"
       :style="editorTopStyle" @click="handleClickEditorContainer">
    <div class="textarea-item-div">
          <span style="position: absolute;top: 6px;left: 15px;color: #bfc4cc;" @click="handleClickEditorContainer"
                v-if="!isFocus && editorContentLength === 0">{{
              $t('loc.IEditorPlaceHolder')
            }}</span>
      <div class="textarea-item p"
           contenteditable="plaintext-only"
           @input="handleInput"
           @focus="handleFocus"
           @blur="handleBlur"
           @keydown="handleKeyDown"
           @compositionstart="onCompositionStart"
           @compositionend="onCompositionEnd"
           @click.stop>
      </div>
    </div>
  </div>
</template>

<script>
import LinkCard from './LinkCard'
import { v4 as uuidV4 } from 'uuid'
import { createApp } from 'vue'
import { IEditorConstants } from '@/utils/EditorTools'

// vue 2
// import Vue from 'vue'
// const linkCard = Vue.extend(LinkCard)
// const linkCard = Vue.extend(LinkCard)
export default {
  name: 'IEditorTextarea',
  components: { LinkCard },
  props: [
    'value',
  ],
  computed: {
    editorTopStyle () {
      // 编辑器模拟聚焦样式
      let simple = { minHeight: '96px' }
      if (this.isFocus) {
        this.$emit('style-change', { borderColor: '#10b3b7', ...simple })
        return { borderColor: '#10b3b7', ...simple }
      }
      this.$emit('style-change', { borderColor: '#dcdfe6', ...simple })
      return { borderColor: '#dcdfe6', ...simple }
    },
  },
  data () {
    return {
      editorInitialized: false, // 编辑器数据初始化
      editorContentLength: 0, //
      isFocus: false, // 编辑器是否聚焦
      inputLock: false, // input 锁
      openVerify: false, // 编辑器是否验空 （校验空方式：值传递到父组件校验）
      isRevokeOrRecover: false, // 撤销、恢复使用标志位
      revoke: [], // ctrl + z 撤销缓存
      recover: [], // ctrl + y 恢复缓存
    }
  },
  watch: {
    value: {
      deep: true,
      immediate: true,
      handler: function (value) {
        if (this.editorInitialized) {
          return
        }
        this.$nextTick(() => {
          let tempDom = document.createElement('div')
          tempDom.innerHTML = value
          // 如果发现富文本结构，则使用富文本
          if (tempDom.querySelector('.textarea-item.p')) {
            tempDom.querySelectorAll('span').forEach(item => {
              (item.id.indexOf('card-parent-') > -1 && item.children[0]) && item.children[0].remove()
            })
            value =  tempDom.querySelector('.textarea-item.p').innerHTML
          }
          this.$el.querySelector('.textarea-item.p').innerHTML = value || ''
          this.editorContentLength = this.$el.querySelector('.textarea-item.p').innerText.trim().length
          this.$nextTick(() => {
            // 监测编辑器 dom 变动
            this.domObserver = new MutationObserver(() => {
              this.$nextTick(() => {
                this.addCloseListener()
              })
              this.syncValue()
            })
            // 挂载监视器
            this.domObserver.observe(this.$el, {
              attributes: true,
              subtree: true,
              childList: true
            })
          })
          this.handleInput()
          this.editorInitialized = true
        })
      }
    },
  },
  methods: {
    onCompositionStart () {
      this.inputLock = true
    },
    onCompositionEnd () {
      this.inputLock = false
      this.handleInput()
    },
    handleClickEditorContainer () {
      this.isFocus = true
      this.$el.querySelector('.textarea-item.p').focus()
    },
    handleKeyDown(event) {
      let selection = document.getSelection()
      let range = selection.getRangeAt(0)
      if (event.code === 'KeyZ' && event.ctrlKey === true && event.altKey === false) {
        if (this.revoke.length > 0) {
          let textareaDom = this.$el.querySelector('.textarea-item.p')
          this.recover.push(this.revoke.pop())
          if (this.revoke.length > 0) {
            let text = this.revoke[this.revoke.length - 1]
            textareaDom.innerHTML = text.content
            this.setPosition(textareaDom, text.range)
          }
          this.isRevokeOrRecover = true
        }
      }
      if (event.code === 'KeyY' && event.ctrlKey === true && event.altKey === false) {
        if (this.recover.length > 0) {
          let textareaDom = this.$el.querySelector('.textarea-item.p')
          let text = this.recover.pop()
          textareaDom.innerHTML = text.content
          this.setPosition(textareaDom, text.range)
          this.revoke.push(text)
          this.isRevokeOrRecover = true
        }
      }
      if (event.code === 'NumpadEnter' || event.code === 'Enter' || event.keyCode === 13 || event.code === 'Space' || event.keyCode === 32) {
        if (range.startOffset === range.endOffset && range.collapsed && range.endContainer === range.startContainer && range.endOffset >= range.startContainer.textContent.trimRight().length) {
          let currentNode = range.startContainer.parentElement || range.startContainer.parentNode
          let nextNode = currentNode.nextSibling
          currentNode.setAttribute('showCard', 'true')
          // 如果下一个节点是 card 节点，光标应直接定位到 card 的下方起始
          if ((nextNode && nextNode.id) && nextNode.id.indexOf('card-parent-') > -1 && nextNode.innerHTML.indexOf('card-') > -1) {
            event.preventDefault()
            // 光标定位到链接节点的开头
            this.setEditorCursor(nextNode.nextSibling, true)
          }
        }
      }
      if (event.code === 'Delete' || event.key === 'Delete') {
        if (range.startOffset === range.endOffset && range.collapsed && range.endContainer === range.startContainer && range.endOffset === range.startContainer.textContent.length) {
          let currentNode = range.startContainer.parentElement || range.startContainer.parentNode
          let nextNode = currentNode.nextSibling
          // 如果下一个节点是 card 节点，光标应直接定位到 card 的下方起始
          if ((nextNode && nextNode.id) && nextNode.id.indexOf('card-parent-') > -1) {
            event.preventDefault()
            // 光标定位到链接节点的开头
            this.setEditorCursor(nextNode.nextSibling, true)
          }
        }
      }
      if (event.key === 'Backspace' || event.code === 'Backspace') {
        if (range.startOffset === 0 && range.endOffset === 0 && range.endContainer === range.startContainer) {
          let brotherNode = range.startContainer.previousSibling
          // 如果上一个节点是 card 节点，光标应直接定位到到链接节点的末尾
          if ((brotherNode && brotherNode.id) && brotherNode.id.indexOf('card-parent-') > -1) {
            // previousElementSibling: 返回元素节点之前的兄弟元素节点（不包括文本节点、注释节点）；
            // previousSibling: 返回元素节点之前的兄弟节点（包括文本节点、注释节点）
            event.preventDefault()
            // 光标定位到链接节点的末尾
            this.setEditorCursor(brotherNode.previousSibling.lastChild, false)
          }
        }
      }
    },
    handleInput () {
      if (this.inputLock) {
        return
      }
      let nodeDom = this.$el.querySelector('.textarea-item.p')
      let cloneNodeDom = this.$el.querySelector('.textarea-item.p').cloneNode(true)
      // 解析文本框节点
      let parsedNodes = []
      let nodeTemp = []
      let newChildList = []
      let cardTemp = []
      // 去除 card 节点
      cloneNodeDom.childNodes.forEach((item, index) => {
        if (item.id && item.id.indexOf('card-') > -1) {
          cardTemp.push(item)
          newChildList.push(document.createTextNode(IEditorConstants.linkCard.linkEndNodePlaceholder))
          return
        }
        // 火狐等浏览器兼容性问题, <br> 转义
        if (item.nodeName === "BR") {
          item = document.createTextNode('\r\n')
        }
        newChildList.push(item)
      })
      // 以 a 标签为界限分割子节点
      newChildList.forEach((item, index) => {
        if (item.nodeName === 'A') {
          if (nodeTemp.length > 0) {
            // 到达分界线, 解析完 push 到 parsedNodes 数组中
            this.parsedNode(nodeTemp).forEach((item, index) => {
              parsedNodes.push(item)
            })
            nodeTemp = []
          }
        }
        if (index === newChildList.length - 1) {
          nodeTemp.push(item)
          this.parsedNode(nodeTemp).forEach((item, index) => {
            parsedNodes.push(item)
          })
          nodeTemp = []
        }
        nodeTemp.push(item)
      })
      let nodeParent = document.createElement('div')
      parsedNodes.forEach((item, index) => {
        nodeParent.appendChild(item)
      })
      // 记录需要挂载的节点 id
      let ids = []
      nodeParent.querySelectorAll('.textarea-item-a').forEach(item => {
            let id = 'card-' + item.id
            let card = cardTemp.find(card => card.id === 'card-parent-' + item.id)
            if (item.getAttribute('showCard').toString() === 'false') {
              return
            }
            if (card) {
              // 如果卡片节点的 url 和链接不相同，则更新卡片
              if (card.getAttribute('url') !== item.href) {
                card.setAttribute('url', item.href)
                ids.push(item.id)
              }
              // 如果
              if (!card.innerHTML.includes(id)) {
                card.innerHTML = '<span id="' + id + '"></span>'
                ids.push(item.id)
              }
              this.insertAfter(card, item)
            } else {
              let dom = document.createElement('span')
              dom.id = 'card-parent-' + item.id
              dom.style.display = 'flex'
              dom.style.pageBreakInside = 'avoid'
              dom.setAttribute('contenteditable', 'false')
              dom.setAttribute('url', item.href)
              dom.innerHTML = '<span id="' + id + '"></span>'
              ids.push(item.id)
              this.insertAfter(dom, item)
            }
          }
      )
      // 格式化编辑器内容（合并 text 节点，使光标定位正确）
      // nodeDom.normalize()
      nodeParent.innerHTML = nodeParent.innerHTML.replace(/;0fj0ew\^2\*5\$@2/g, '') // ;0fj0ew^2*5$@2
      let position = this.getPosition(nodeDom)
      nodeDom.innerHTML = nodeParent.innerHTML
      this.setPosition(nodeDom, position)
      // 挂载 card
      let a_Nodes = nodeDom.querySelectorAll('.textarea-item-a')
      a_Nodes.forEach(item => {
            if (ids.includes(item.id)) {
              this.generateCards(item)
            }
          }
      )
      // 如果本次 input 事件为 ctrl + z 或者 ctrl + y 事件，那么不处理
      if (!this.isRevokeOrRecover && nodeDom.innerHTML !== this.revoke[this.revoke.length - 1]) {
        // 将当前编辑器内容放入撤销缓存
        this.revoke.push({ content: nodeDom.innerHTML, range: position })
        this.revoke.length > 10 && this.revoke.shift()
        this.Recover = []
      }
      this.isRevokeOrRecover && (this.isRevokeOrRecover = false)
      this.syncValue()
    },
    // 给卡片按钮增加关闭事件
    addCloseListener () {
      let closeDomes = this.$el.querySelectorAll('.i-editor-card-close')
      closeDomes.forEach(item => {
        item.onclick = (pointerEvent) => {
          if (pointerEvent.target) {
            let id = pointerEvent.target.id.replace('remove-', '')
            if (id) {
              let dom = document.getElementById('card-parent-' + id)
              dom.remove()
              this.handleCloseCard(id)
            }
          }
        }
      })
    },
    insertAfter (newElement, targetElement) {
      let parent = targetElement.parentNode//获取目标节点的父级标签
      if (parent.lastChild === targetElement) {//如果目标节点正好是最后一个节点，使用appendChild插入
        parent.appendChild(newElement)
      } else {
        parent.insertBefore(newElement, targetElement.nextSibling)//一般情况下要取得目标节点的下一个节点，再使用insertBefore()方法。
      }
    },
    generateCards (item) {
      // 挂载 card
      let data = {
        id: item.id,
        link: item.href,
        cardClose: this.handleCloseCard
      }
      // new linkCard(data).$mount('#card-' + item.id)
      createApp(LinkCard, data).mount('#card-' + item.id)
    },
    handleCloseCard (id) {
      // 将对应 id 链接的标签属性 showCard 置为 false
      this.$nextTick(() => {
        let linkDom = document.getElementById(id)
        linkDom.setAttribute('showCard', 'false')
        this.insertAfter(document.createTextNode(' '), linkDom)
      })
    },
    parsedNode (nodeTemp) {
      if (!nodeTemp || nodeTemp.length === 0) {
        return []
      }
      // 拿到 doms 节点中的文本
      let nodeParent = document.createElement('div')
      nodeTemp.forEach((item, index) => {
        nodeParent.appendChild(item.cloneNode(true))
      })
      // 格式化 dom 中 text 节点
      nodeParent.innerHTML = this.formatContent(nodeParent.textContent)
      // 匹配是否是首个链接
      let find = true
      nodeParent.childNodes.forEach((item, index) => {
        if (item.nodeName === 'A') {
          item.id = uuidV4()
          item.setAttribute('showCard', 'ture')
          if (nodeTemp[0].nodeName === 'A' && find) {
            item.id = nodeTemp[0].id
            item.href === nodeTemp[0].href && item.setAttribute('showCard', nodeTemp[0].getAttribute('showCard'))
            find = false
          }
        }
      })
      return nodeParent.childNodes
    },
    // 获取光标位置
    getPosition (dom) {
      // 编辑器未初始化不获取光标
      if (!this.editorInitialized) {
        return
      }
      let range = document.getSelection().getRangeAt(0)
      let node = range.commonAncestorContainer
      let offset = range.startOffset
      let position = 0
      let childNodes = dom.childNodes
      for (const childNode of childNodes) {
        // 过滤 card 节点
        if (childNode.id && childNode.id.indexOf('card-') > -1) {
          continue
        }
        if (childNode === node) {
          position += offset
          break
        }
        if (childNode.contains(node)) {
          position += this.getPosition(childNode)
          break
        }
        position += childNode.textContent.length
      }
      return position
    },
    // 设置光标位置
    setPosition (dom, position) {
      // 编辑器未初始化不设置光标
      if (!this.editorInitialized) {
        return
      }
      let { node, offset } = this.getDeepestNode(dom, position)
      let selection = document.getSelection()
      let range = document.createRange()
      range.setEnd(node, offset)
      range.collapse()
      // 清除所有光标对象
      selection.removeAllRanges()
      // 添加新的光标对象
      selection.addRange(range)
    },
    getDeepestNode (dom, position) {
      if (dom.nodeType === Node.TEXT_NODE) {
        return { node: dom, offset: position }
      }
      let childNodes = dom.childNodes
      for (const childNode of childNodes) {
        // 过滤 card 节点
        if (childNode.id && childNode.id.indexOf('card-') > -1) {
          continue
        }
        let length = childNode.textContent.length
        if (position <= length) {
          return this.getDeepestNode(childNode, position)
        }
        position -= length
      }
      return { node: dom, offset: position }
    },
    handleBlur () {
      this.isFocus = false
      this.openVerify = true
    },
    handleFocus () {
      this.isFocus = true // 为了校验失败的样式
    },
    // 设置光标
    setEditorCursor (node, toStart) {
      if (!node) {
        return
      }
      this.$nextTick(() => {
        (node.focus) && node.focus()
        let selection = document.getSelection()
        let range = document.createRange()
        range.selectNodeContents(node)
        range.collapse(toStart)
        // !toStart && range.setStart(node, node.childNodes.length)
        // !toStart && range.setEnd(node, node.childNodes.length)
        // 清除所有光标对象
        selection.removeAllRanges()
        // 添加新的光标对象
        selection.addRange(range)
      })
    },
    setLinkInfo (item, value) {
      item.linkInfo = value
    },
    formatContent (content) {
      let reg = /(((https?):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|win|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2})))|(www\.)+[a-zA-Z0-9-]+\.(com|win|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?@'\\+:&%$#=~_-]+))*(\/)?(\s|$|\s$|;0fj0ew\^2\*5\$@2)/ig
      return content.replaceAll(reg, match => {
        let appendText = ''
        let href = match.trim()
        // 空白符或者回车结尾均为空白符
        if (/(;0fj0ew\^2\*5\$@2)$/i.test(match)) { // 以 www 开头的链接要手动拼接 https:// 前缀
          appendText = IEditorConstants.linkCard.linkEndNodePlaceholder

          match = match.replace(/(;0fj0ew\^2\*5\$@2)$/i, '')
          href = href.replace(/(;0fj0ew\^2\*5\$@2)$/i, '')
        }
        href = href.trim().replace(/\/$/, '')
        if (!/(http)/i.test(href)) { // 以 www 开头的链接要手动拼接 https:// 前缀
          href = 'https://' + href
        }
        return `<a class="textarea-item-a font-color-primary" target="_blank" href="${href}">${match}</a>` + appendText
      })
    },
    syncValue () {
      let textareaDom = this.$el.querySelector('.textarea-item.p')
      let lastChild = textareaDom.lastChild
      if (lastChild && lastChild.id && lastChild.id.indexOf('card-parent-') > -1 && lastChild.innerHTML.indexOf('card-') > -1) {
        textareaDom.appendChild(document.createTextNode(' '))
      }
      // 计算长度 this.editorContentLength
      this.editorContentLength = textareaDom.innerText.trim().length
      if (!this.openVerify) {
        return
      }
      let description = this.editorContentLength !== 0 && this.$el.querySelector('.textarea-item-div').innerHTML || ''
      this.$emit('input', description)
      this.$emit('change', description)
    },
  }
}
</script>

<style lang="less" scoped>
.is-error .i-editor-textarea {
  border-color: #f56c6c;
}

.i-editor-textarea {
  line-height: 18px;
  display: flex;
  flex-flow: column;
  cursor: text;
  // 兼容 Safari 浏览器
  -webkit-user-select: text;
  user-select: text;
  margin: 0 0 10px;
  height: 100%;
}

.textarea-item {
  padding: 0 15px;
  -webkit-user-modify: read-write-plaintext-only;
}

.textarea-item.p {
  height: 100%;
  width: 100%;
  word-break: break-word;
  // 兼容 Safari 浏览器
  -webkit-user-select: text;
  user-select: text;
  cursor: text;
  // 兼容 Safari、火狐（换行失效）
  white-space: break-spaces;
  display: inline-block;
}

/deep/ .textarea-item.p a, a:hover, a:focus {
  cursor: text;
  color: #10B3B7;
  word-break: break-all;
}

.textarea-item-div {
  padding-top: 5px;
  height: 100%;
}
.i-editor-textarea p {
  width: calc(100% - 30px);
  line-height: 1.5;
}
</style>